﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ca2simulator.shared;
using System.Threading;

namespace ca2simulator.server.simulator
{
    // TODO Possible optimizations: LUT for transfer function

    public class BasicSimulator : ISimulator
    {
        private CA2 _ca;
        private Lattice _nextLattice;
        private List<ISimulatorListener> _Listeners = new List<ISimulatorListener>();

        private Thread _worker;
        private SimulatorThreader _threader;

        public BasicSimulator(CA2 ca)
        {
            this._ca = ca;
            _nextLattice = new Lattice(_ca.Lattice.InitialState, _ca.Lattice.Rows, _ca.Lattice.Columns);
            _ca.initialize();

            _threader = new SimulatorThreader(this);
        }

        public void Step(int numSteps)
        {
            for (int i = 0; i < numSteps; i++) Step();
        }

        protected void StepSimulation()
        {
            int rows = _ca.Lattice.Rows, cols = _ca.Lattice.Columns;
            Cell[,] next = _nextLattice.Cells;
            Cell[,] orig = _ca.Lattice.Cells;
            ITransferFunc trans = _ca.Transfer;
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j<cols; j++)
                {
                    transferCell(i, j, next, orig, trans);
                }
            }
            Lattice tempLat = _ca.Lattice;
            _ca.Lattice = _nextLattice;
            _nextLattice = tempLat;
            // Tell listeners a step has completed
            announceUpdate();
        }

        public void Step()
        {
            _worker = new Thread(StepSimulation);
            _worker.Start();
        }

        public void Run()
        {
            _worker = new Thread(_threader.start);
            _worker.Start();
            
        }

        public void Halt()
        {
            _threader.stop();
            if (!_worker.Join(1000 * 5))
            {
                Console.Out.WriteLine("BasicSimulator::Halt() - Worker thread would not halt, error");
            }
        }

        public Lattice GetLattice()
        {
            return _ca.Lattice;
        }

        public bool AddListener(ISimulatorListener listener) 
        {
            _Listeners.Add(listener);
            return true;
        }
        public bool RemoveListener(ISimulatorListener listener) {
            return _Listeners.Remove(listener);
        }

        /* Helper functions*/

        private void transferCell(int r, int c, Cell[,] target, Cell[,] orig, ITransferFunc transfer)
        {
            target[r,c].CellState = transfer.NextState(orig, r, c);
        }
        private void announceUpdate()
        {
            foreach (ISimulatorListener l in _Listeners) l.OnLatticeUpdate();
        }
        private void announceFinish()
        {
            foreach (ISimulatorListener l in _Listeners) l.OnSimulationFinish() ;
        }

        class SimulatorThreader
        {
            private BasicSimulator _sim;
            private volatile bool isRunning = false;
            public SimulatorThreader(BasicSimulator sim)
            {
                _sim = sim;
            }
            public void start()
            {
                isRunning = true;
                while (isRunning)
                {
                    _sim.StepSimulation();
                    Thread.Sleep(50);
                }
            }
            public void stop()
            {
                isRunning = false;
            }
        }
    }

    


}